QuickTime 4 Reference
This appendix includes sample code from QTWiredSprite.c . Note this is only a partial code listing. You can download the full sample code at QuickTime Web site at http://www.apple.com/quicktime/developers/samplecode.html#sprites .) It is also available on the QuickTime SDK.
For information on how to use this sample code in your application, see Chapter 2, "The Sprite Media Handler," and Chapter 3, "Authoring Wired Movies and Sprite Animations."
//////////
//
// File: QTWiredSprites.c
//
// Contains: QuickTime wired sprites support for QuickTime movies.
//
// Written by: Sean Allen
// Revised by: Chris Flick and Tim Monroe
// Based (heavily!) on the existing MakeActionSpriteMovie.c
// code written by Sean Allen.
//
// Copyright: © 1997-1999 by Apple Computer, Inc., all rights reserved.
//
// Change History (most recent first):
//
// <2> 03/26/98 rtm made fixes for Windows compiles
// <1> 03/25/98 rtm first file; integrated existing code
// with shell framework
//
//
// This sample code creates a wired sprite movie containing one sprite
//track. The sprite track contains six sprites: two penguins and four
//buttons.
//
// The four buttons are initially invisible. When the mouse enters (or
// "rolls over") a button, it appears.
// When the mouse is clicked inside a button, its image changes to its ///
"pressed" image. When the mouse
// is released, its image changes back to its "unpressed" image. If the //
mouse is released inside the button,
// an action is triggered. The buttons perform the actions of go to
// beginning of movie, step backward,
// step forward, and go to end of movie.
//
// The first penguin shows all of the buttons when the mouse enters it,
// and hides them when the mouse exits.
// The first penguin is the only sprite that has properties that are
// overriden by the override sprite samples.
// These samples override its matrix (in order to move it) and its image
// index (in order to make it "waddle").
//
// When the mouse is clicked on the second penguin, it changes its image
// index to its "eyes closed" image.
// When the mouse is released, it changes back to its normal image. This
// makes it appear to blink when clicked on.
// When the mouse is released over the penguin, several actions are
// triggered. Both penguins' graphics states are
// toggled between copyMode and blendMode, and the movie's rate is
// toggled between zero and one.
//
// The second penguin moves once per second. This occurs whether the
// movie's rate is currently zero or one,
// because it is being triggered by a gated idle event. When the penguin
// receives the idle event, it changes
// its matrix using an action which uses min, max, delta, and wraparound
// options.
//
// The movie's looping mode is set to palindrome by a frame-loaded
// action.
//
// So, our general strategy is as follows (though perhaps not in the
// order listed):
//
// (1) Create a new movie file with a single sprite track.
// (2) Assign the "no controller" movie controller to the movie.
// (3) Set the sprite track's background color, idle event
// frequency, and hasActions properties.
// (4) Convert our PICT resources to animation codec images with
// transparency.
// (5) Create a key frame sample containing six sprites and all of
// their shared images.
// (6) Assign the sprites their initial property values.
// (7) Create a frameLoaded event for the key frame.
// (8) Create some override samples that override the matrix and
// image index properties of the first penguin sprite.
//
// NOTES:
//
// *** (1) ***
// There are event types other that mouse related events (for instance,
// Idle and FrameLoaded).
// Idle events are independent of the movie's rate, and they can be
// gated so they are send at most
// every n ticks. In our sample movie, the second penguin moves when the
// movie's rate is zero,
// and moves only once per second because of the value of the sprite
// track's idleEventFrequencey property.
//
// *** (2) ***
// Multiple actions may be executed in response to a single event. In
// our sample movie, rolling over
// the first penguin shows and hides four different buttons.
//
// *** (3) ***
// Actions may target any sprite or track in the movie. In our sample
// movie, clicking on one penguin
// changes the graphics mode of the other.
//
// *** (4) ***
// Conditional and looping control structures are supported. In our
// sample movie, the second penguin
// uses the "case statement" action.
//
// *** (5) ***
// Sprite track variables that have not been set have a default value of
// zero. (The second penguin's
// conditional code relies on this.)
//
// *** (6) ***
// Wired sprites were previously known as "action sprites". Don't let
// the names of some of the utility
// functions confuse you. We'll try to update the source code as time
// permits.
//
// *** (7) ***
// Penguins don't fly, but I hear they totally shred halfpipes on
// snowboards.
//
//////////
// header files
#include "QTWiredSprites.h"
//////////
//
// QTWired_CreateWiredSpritesMovie
// Create a QuickTime movie containing a wired sprites track.
//
//////////
OSErr QTWired_CreateWiredSpritesMovie (void)
{
short myResRefNum = 0;
Movie myMovie = NULL;
Track myTrack;
Media myMedia;
StandardFileReply myReply;
QTAtomContainer mySample = NULL;
QTAtomContainer myActions = NULL;
QTAtomContainer myBeginButton, myPrevButton, myNextButton,
myEndButton;
QTAtomContainer myPenguinOne, myPenguinTwo,
myPenguinOneOverride;
QTAtomContainer myBeginActionButton, myPrevActionButton,
myNextActionButton, myEndActionButton;
QTAtomContainer myPenguinOneAction, myPenguinTwoAction;
RGBColor myKeyColor;
Point myLocation;
short isVisible, myLayer, myIndex, myResID, i,
myDelta;
Boolean hasActions;
long myFlags = createMovieFileDeleteCurFile |
createMovieFileDontCreateResFile;
OSType myType = FOUR_CHAR_CODE('none');
UInt32 myFrequency;
QTAtom myEventAtom;
long myLoopingFlags;
ModifierTrackGraphicsModeRecord myGraphicsMode;
OSErr myErr = noErr;
//////////
//
// create a new movie file and set its controller type
//
//////////
// ask the user for the name of the new movie file
StandardPutFile("\pSprite movie file name:", "\pSprite.mov",
&myReply);
if (!myReply.sfGood)
goto bail;
// create a movie file for the destination movie
myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), 0,
myFlags, &myResRefNum, &myMovie);
if (myErr != noErr)
goto bail;
// select the "no controller" movie controller
myType = EndianU32_NtoB(myType);
SetUserDataItem(GetMovieUserData(myMovie), &myType, sizeof(myType),
kUserDataMovieControllerType, 1);
//////////
//
// create the sprite track and media
//
//////////
myTrack = NewMovieTrack(myMovie, ((long)kSpriteTrackWidth << 16),
((long)kSpriteTrackHeight << 16), kNoVolume);
myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0);
//////////
//
// create a key frame sample containing six sprites and all of their
// shared images
//
//////////
// create a new, empty key frame sample
myErr = QTNewAtomContainer(&mySample);
if (myErr != noErr)
goto bail;
myKeyColor.red = 0xffff; // white
myKeyColor.green = 0xffff;
myKeyColor.blue = 0xffff;
// add images to the key frame sample
AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonUp,
&myKeyColor, kGoToBeginningButtonUpIndex, NULL, NULL);
AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonDown,
&myKeyColor, kGoToBeginningButtonDownIndex, NULL, NULL);
AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonUp, &myKeyColor,
kGoToEndButtonUpIndex, NULL, NULL);
AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonDown,
&myKeyColor, kGoToEndButtonDownIndex, NULL, NULL);
AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonUp,
&myKeyColor, kGoToPrevButtonUpIndex, NULL, NULL);
AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonDown,
&myKeyColor, kGoToPrevButtonDownIndex, NULL, NULL);
AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonUp,
&myKeyColor, kGoToNextButtonUpIndex, NULL, NULL);
AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonDo
bail:
if (mySample != NULL)
QTDisposeAtomContainer(mySample);
if (myBeginButton != NULL)
QTDisposeAtomContainer(myBeginButton);
if (myPrevButton != NULL)
QTDisposeAtomContainer(myPrevButton);
if (myNextButton != NULL)
QTDisposeAtomContainer(myNextButton);
if (myEndButton != NULL)
QTDisposeAtomContainer(myEndButton);
if (myResRefNum != 0)
CloseMovieFile(myResRefNum);
if (myMovie != NULL)
DisposeMovie(myMovie);
return(myErr);
}
//////////
//
// QTWired_AddPenguinTwoConditionalActions
// Add actions to the second penguin that transform him (her?) into a two
// state button
// that plays or pauses the movie.
//
// We are relying on the fact that a "GetVariable" for a variable ID
// which has never been set
// will return zero. If we needed a different default value, we could
// initialize it using the
// frameLoaded event.
//
// A higher-level description of the logic is:
//
// On MouseUpInside
// If (GetVariable(DefaultTrack, 1) = 0)
// SetMovieRate(1)
// SetSpriteGraphicsMode(DefaultSprite, { blend, grey } )
// SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
// { ditherCopy, white } )
// SetVariable(DefaultTrack, 1, 1)
// ElseIf (GetVariable(DefaultTrack, 1) = 1)
// SetMovieRate(0)
// SetSpriteGraphicsMode(DefaultSprite, { ditherCopy, white })
// SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
// { blend, grey })
// SetVariable(DefaultTrack, 1, 0)
// Endif
// End
//
//////////
OSErr QTWired_AddPenguinTwoConditionalActions (QTAtomContainer
theContainer, QTAtom theEventAtom)
{
QTAtom myNewActionAtom, myNewParamAtom, myConditionalAtom;
QTAtom myExpressionAtom, myOperatorAtom, myActionListAtom;
short myParamIndex, myConditionIndex, myOperandIndex;
float myConstantValue;
QTAtomID myVariableID;
ModifierTrackGraphicsModeRecord myBlendMode, myCopyMode;
OSErr myErr = noErr;
myBlendMode.graphicsMode = blend;
myBlendMode.opColor.red = myBlendMode.opColor.green =
myBlendMode.opColor.blue = 0x8fff; // grey
myCopyMode.graphicsMode = ditherCopy;
myCopyMode.opColor.red = myCopyMode.opColor.green =
myCopyMode.opColor.blue = 0xffff; // white
AddActionAtom(theContainer, theEventAtom, kActionCase,
&myNewActionAtom);
myParamIndex = 1;
AddActionParameterAtom(theContainer, myNewActionAtom, myParamIndex,
0, NULL, &myNewParamAtom);
// first condition
myConditionIndex = 1;
AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
&myConditionalAtom);
AddExpressionContainerAtomType(theContainer, myConditionalAtom,
&myExpressionAtom);
AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
&myOperatorAtom);
myOperandIndex = 1;
myConstantValue = kButtonStateOne;
AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
myOperandIndex, NULL, myConstantValue);
myOperandIndex = 2;
myVariableID = kPenguinStateVariableID;
AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
0, NULL, 0, myVariableID);
AddActionListAtom(theContainer, myConditionalAtom,
&myActionListAtom);
AddMovieSetRateAction(theContainer, myActionListAtom, 0,
Long2Fix(1));
AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
NULL, 0, 0, NULL, &myBlendMode, NULL);
AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
&myCopyMode, NULL);
AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
kPenguinStateVariableID, kButtonStateTwo, 0, NULL, 0);
// second condition
myConditionIndex = 2;
AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
&myConditionalAtom);
AddExpressionContainerAtomType(theContainer, myConditionalAtom,
&myExpressionAtom);
AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
&myOperatorAtom);
myOperandIndex = 1;
myConstantValue = kButtonStateTwo;
AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
myOperandIndex, NULL, myConstantValue);
myOperandIndex = 2;
myVariableID = kPenguinStateVariableID;
AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
0, NULL, 0, myVariableID);
AddActionListAtom(theContainer, myConditionalAtom,
&myActionListAtom);
AddMovieSetRateAction(theContainer, myActionListAtom, 0,
Long2Fix(0));
AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
NULL, 0, 0, NULL, &myCopyMode, NULL);
AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
&myBlendMode, NULL);
AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
kPenguinStateVariableID, kButtonStateOne, 0, NULL, 0);
bail:
return(myErr);
}
//////////
//
// QTWired_AddWraparoundMatrixOnIdle
// Add beginning, end, and change matrices to the specified atom
// container.
//
//////////
OSErr QTWired_AddWraparoundMatrixOnIdle (QTAtomContainer theContainer)
{
MatrixRecord myMinMatrix, myMaxMatrix, myDeltaMatrix;
long myFlags = kActionFlagActionIsDelta |
kActionFlagParameterWrapsAround;
QTAtom myActionAtom;
OSErr myErr = noErr;
myMinMatrix.matrix[0][0] = myMinMatrix.matrix[0][1] =
myMinMatrix.matrix[0][2] = EndianS32_NtoB(0xffffffff);
myMinMatrix.matrix[1][0] = myMinMatrix.matrix[1][1] =
myMinMatrix.matrix[1][2] = EndianS32_NtoB(0xffffffff);
myMinMatrix.matrix[2][0] = myMinMatrix.matrix[2][1] =
myMinMatrix.matrix[2][2] = EndianS32_NtoB(0xffffffff);
myMaxMatrix.matrix[0][0] = myMaxMatrix.matrix[0][1] =
myMaxMatrix.matrix[0][2] = EndianS32_NtoB(0x7fffffff);
myMaxMatrix.matrix[1][0] = myMaxMatrix.matrix[1][1] =
myMaxMatrix.matrix[1][2] = EndianS32_NtoB(0x7fffffff);
myMaxMatrix.matrix[2][0] = myMaxMatrix.matrix[2][1] =
myMaxMatrix.matrix[2][2] = EndianS32_NtoB(0x7fffffff);
myMinMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((1 *
kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
myMaxMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((3 *
kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
SetIdentityMatrix(&myDeltaMatrix);
myDeltaMatrix.matrix[2][1] = Long2Fix(1);
// change location
myErr = AddSpriteSetMatrixAction(theContainer,
kParentAtomIsContainer, kQTEventIdle, 0, NULL, 0, 0, NULL,
&myDeltaMatrix, &myActionAtom);
if (myErr != noErr)
goto bail;
myErr = AddActionParameterOptions(theContainer, myActionAtom, 1,
myFlags, sizeof(myMinMatrix), &myMinMatrix,
sizeof(myMaxMatrix), &myMaxMatrix);
bail:
return(myErr);
}